home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / MiscSearchText.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  10.3 KB  |  370 lines

  1. /*
  2.  *  Copyright (c) 1993 Christopher J. Kane.  All rights reserved.
  3.  *
  4.  *  This software is subject to the terms of the MiscKit license
  5.  *  agreement.  Refer to the license document included with the
  6.  *  MiscKit distribution for these terms.
  7.  *
  8.  *  Version: 1.2 (25 June 1994)
  9.  *
  10.  *  Licinda Woudberg - licinda@Black_Albatross.otago.ac.nz
  11.  *    25 June 1994
  12.  *    Changed: -replaceAll.... works so that it can handle both plain
  13.  *        text & rtf style document.  Still need to double check rtfd's
  14.  *    Changed: -replaceSelection:
  15.  *        now sends textDidChange: notice to the delegate
  16.  */
  17.  
  18. #import <misckit/MiscSearchText.h>
  19. #import <misckit/MiscTBMK.h>
  20. #import <misckit/regexpr.h>
  21.  
  22. @implementation Text (SearchText)
  23.  
  24. - (oneway void)makeSelectionVisible
  25. {
  26.   [self scrollSelToVisible];
  27. }
  28.  
  29. - (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
  30. {
  31.     unsigned char       fm[256], tr[256];
  32.     struct re_pattern_buffer rpat;
  33.     Misc_TBMKpattern    lpat = NULL;
  34.     NXStream           *in_strm = NULL;
  35.     int                 s1=0, e1=0, s2=0, e2=0, ret_val=0, plen=0, rlen=0, pos=0, size=0, p=0, searchTextMaxSize=0;
  36.  
  37. /* Known bug: the delegate will not get textWillChange:, textDidChange: notification messages */
  38.     if ( (sp0.cp < 0) && (mode != TextEdgeToTextEdge) )
  39.         return SEARCH_NO_SELECTION;
  40.     if ( ![self isEditable] )
  41.         return SEARCH_CANNOT_WRITE;
  42.     switch ( mode )                             /* setup start and end points for the search */
  43.         {
  44.         case TextEdgeToTextEdge:
  45.         case SelStartToSelStart:
  46.         case SelEndToSelEnd:
  47.             s1 = 0;            e1 = textLength;            break;
  48.         case TextEdgeToSelStart:
  49.             s1 = 0;            e1 = sp0.cp;                break;
  50.         case TextEdgeToSelEnd:
  51.             s1 = 0;            e1 = spN.cp;                break;
  52.         case SelStartToSelEnd:
  53.             s1 = sp0.cp;    e1 = spN.cp - sp0.cp;        break;
  54.         case SelStartToTextEdge:
  55.             s1 = sp0.cp;    e1 = textLength - sp0.cp;    break;
  56.         case SelEndToTextEdge:
  57.             s1 = spN.cp;    e1 = textLength - spN.cp;    break;
  58.         case SelEndToSelStart:
  59.             s1 = 0;            e1 = sp0.cp;
  60.             s2 = spN.cp;    e2 = textLength - spN.cp;    break;
  61.         default:
  62.             return SEARCH_INVALID_ARGUMENT;
  63.         }
  64.     searchTextMaxSize = s1 + e1;
  65.     plen = strlen(pattern);                    /* pattern length */
  66.     rlen = strlen(replacement);                /* replacement length */
  67.     if (regexpr)
  68.         {
  69.         /* dealing with a regular expression */
  70.         char *str;
  71.         int i;
  72.  
  73.         memset(&rpat, 0, sizeof(rpat));
  74.         for ( i = 256; i--; )
  75.             tr[i] = i;
  76.         if (!cases)
  77.             for ( i = 'A'; i <= 'Z'; i++ )
  78.                 tr[i] = i - 'A' + 'a';
  79.         rpat.translate = tr;
  80.         rpat.fastmap = fm;
  81.         str = re_compile_pattern((char *)pattern, plen, &rpat);
  82.         if (str != NULL)
  83.             return (strcmp(str, "Out of memory") ? SEARCH_INVALID_REGEXPR : SEARCH_INTERNAL_ERROR);
  84.         }
  85.     else
  86.         {
  87.         /* dealing with a normal find & replace - setup the Pattern to search for */
  88.         lpat = Misc_TBMKpattern_alloc(pattern, plen, 0, !cases);
  89.         if (lpat == NULL)
  90.             return SEARCH_INTERNAL_ERROR;
  91.         }
  92.     in_strm = NXOpenMemory(NULL, 0, NX_READWRITE);    /* open the stream */
  93.     if (in_strm == NULL)
  94.         {
  95.         ret_val = SEARCH_INTERNAL_ERROR;
  96.         goto exit;
  97.         }
  98.     [self writeText:in_strm];                /* read the text into the stream */
  99.     NXSeek(in_strm, 0, NX_FROMSTART);        /* start at the begining */
  100.     ret_val = 0;                            /* set the counter to zero */
  101.  
  102.  
  103. start_searching:
  104.     if (NXUserAborted())
  105.         {
  106.         ret_val = SEARCH_ABORTED;
  107.         goto exit;
  108.         }
  109.     if (regexpr)
  110.         {
  111.         /* regular expression search */
  112.         p = -1;
  113.         if ( s1 > e1)
  114.             /* reached the end of the search area */
  115.             goto exit;
  116.         if ( s1 >= 0 )
  117.             {
  118.             searchTextMaxSize = s1 + e1;
  119.             p = re_search_pattern(&rpat, in_strm->buf_base, searchTextMaxSize, s1, e1, 0);
  120.             }
  121.         if (p==-1 && e2!=0)
  122.             {
  123.             s1 = s2;
  124.             e1 = e2;
  125.             s2 = e2 = 0;
  126.             goto start_searching;
  127.             }
  128.         if (p==-2)
  129.             {
  130.             ret_val = SEARCH_INTERNAL_ERROR;
  131.             goto exit;
  132.             }
  133.         if (p>-1)
  134.             {
  135.             int newTextPos=0;
  136.             
  137.             pos = p;
  138.             size = re_match_pattern(&rpat, in_strm->buf_base, searchTextMaxSize, pos, 0);
  139.             if (size<0)
  140.                 {
  141.                 ret_val = SEARCH_INTERNAL_ERROR;
  142.                 goto exit;
  143.                 }
  144.             newTextPos = (ret_val * (rlen - size)) + pos;
  145.             [self setSel:newTextPos :(newTextPos+size) ];
  146.             [self makeSelectionVisible];
  147.             [self replaceSel:replacement];
  148.             s1 = p + rlen;
  149.             ret_val++;
  150.             goto start_searching;
  151.             }
  152.         }
  153.     else
  154.         {
  155.         /* normal find & replace search */
  156.         p = 0;
  157.         if (s1 > e1)
  158.             /* reached the end of the search area */
  159.             goto exit;
  160.         if (s1 >= 0)
  161.             p = Misc_TBMKsearch_memory(lpat, in_strm->buf_base + s1, e1, 0, &pos);
  162.         if (p < 0)
  163.             {
  164.             /* couldn't find a match for some reason */
  165.             ret_val = SEARCH_INTERNAL_ERROR;
  166.             goto exit;
  167.             }
  168.         if (p == 0 && e2 != 0)
  169.             {
  170.             /* couldn't find a match in the 1st selection area - there is a 2nd area to check */
  171.             s1 = s2;
  172.             e1 = e2;
  173.             s2 = e2 = 0;
  174.             goto start_searching;
  175.             }
  176.         if (p > 0)
  177.             {
  178.             /* found a match */
  179.             int newTextPos=0;
  180.             
  181.             pos = pos + s1;        /* set selection variables */
  182.             newTextPos = (ret_val * (rlen - plen)) + pos;
  183.             size = plen;
  184.             ret_val++;
  185.             [self setSel:newTextPos :(newTextPos+size) ];            /* select the area */
  186.             [self makeSelectionVisible];
  187.             [self replaceSel:replacement];        /* replace the area */
  188.             s1 = pos + plen;            /* reset the start position */
  189.             goto start_searching;
  190.             }
  191.         }
  192.  
  193. exit:
  194.     Misc_TBMKpattern_free(&lpat);
  195.     if (in_strm != NULL)
  196.         NXCloseMemory(in_strm, NX_FREEBUFFER);
  197.     return ret_val;
  198. }
  199.  
  200. - (oneway void)replaceSelection:(const char *)replacement
  201. {
  202.     if ([self isEditable]) {
  203.         if (delegate && [delegate respondsTo:@selector(textWillChange:)])
  204.             [delegate textWillChange:self];
  205.         [self replaceSel:replacement];
  206.         if (delegate && [delegate respondsTo:@selector(textDidChange:)])
  207.             [delegate textDidChange:self];
  208.     }
  209. }
  210.  
  211. - (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
  212. {
  213.   unsigned char fm[256], tr[256];
  214.   struct re_pattern_buffer rpat;
  215.   Misc_TBMKpattern lpat=NULL;
  216.   NXStream *in_strm=NULL;
  217.   int s1=0, e1=0, s2=0, e2=0, ret_val, plen, p, position;
  218.  
  219.   if (sp0.cp<0 && mode!=TextEdgeToTextEdge)
  220.     return SEARCH_NO_SELECTION;
  221.   if (rev)
  222.     switch (mode)
  223.       {
  224.         case TextEdgeToSelStart: s1 = textLength-1; e1 = sp0.cp-textLength; break;
  225.         case TextEdgeToSelEnd: s1 = textLength-1; e1 = spN.cp-textLength; break;
  226.         case TextEdgeToTextEdge: s1 = textLength-1; e1 = -textLength; break;
  227.         case SelStartToSelEnd: s1 = sp0.cp-1; e1 = -sp0.cp+1; s2 = textLength-1; e2 = spN.cp-textLength+1; break;
  228.         case SelStartToTextEdge: s1 = sp0.cp-1; e1 = -sp0.cp+1; break;
  229.         case SelStartToSelStart: s1 = sp0.cp-1; e1 = -sp0.cp+1; s2 = textLength-1; e2 = sp0.cp-textLength+1; break;
  230.         case SelEndToTextEdge: s1 = spN.cp-1; e1 = -spN.cp+1; break;
  231.         case SelEndToSelStart: s1 = spN.cp-1; e1 = sp0.cp-spN.cp+1; break;
  232.         case SelEndToSelEnd: s1 = spN.cp-1; e1 = -spN.cp+1; s2 = textLength-1; e2 = spN.cp-textLength+1; break;
  233.         default: return SEARCH_INVALID_ARGUMENT;
  234.       }
  235.   else
  236.     switch (mode)
  237.       {
  238.         case TextEdgeToSelStart: s1 = 0; e1 = sp0.cp; break;
  239.         case TextEdgeToSelEnd: s1 = 0; e1 = spN.cp; break;
  240.         case TextEdgeToTextEdge: s1 = 0; e1 = textLength; break;
  241.         case SelStartToSelEnd: s1 = sp0.cp; e1 = spN.cp-sp0.cp; break;
  242.         case SelStartToTextEdge: s1 = sp0.cp; e1 = textLength-sp0.cp; break;
  243.         case SelStartToSelStart: s1 = sp0.cp; e1 = textLength-sp0.cp; s2 = 0; e2 = sp0.cp; break;
  244.         case SelEndToTextEdge: s1 = spN.cp; e1 = textLength-spN.cp;  break;
  245.         case SelEndToSelStart: s1 = spN.cp; e1 = textLength-spN.cp; s2 = 0; e2 = sp0.cp; break;
  246.         case SelEndToSelEnd: s1 = spN.cp; e1 = textLength-spN.cp; s2 = 0; e2 = spN.cp; break;
  247.         default: return SEARCH_INVALID_ARGUMENT;
  248.       }
  249.   plen = strlen(pattern);
  250.   if (regexpr)
  251.     {
  252.       char *str;
  253.       int i;
  254.       memset(&rpat, 0, sizeof(rpat));
  255.       for(i=256; i--;)
  256.         tr[i] = i;
  257.       if (!cases)
  258.         for(i='A'; i<='Z'; i++) tr[i] = i-'A'+'a';
  259.       rpat.translate = tr;
  260.       rpat.fastmap = fm;
  261.       str = re_compile_pattern((char *)pattern, plen, &rpat);
  262.       if (str!=NULL)
  263.         return (strcmp(str, "Out of memory")?SEARCH_INVALID_REGEXPR:SEARCH_INTERNAL_ERROR);
  264.     }
  265.   else
  266.     {
  267.       lpat = Misc_TBMKpattern_alloc(pattern, plen, rev, !cases);
  268.       if (lpat==NULL)
  269.         return SEARCH_INTERNAL_ERROR;
  270.     }
  271.   in_strm = NXOpenMemory(NULL, 0, NX_READWRITE);
  272.   if (in_strm==NULL)
  273.     {
  274.       ret_val = SEARCH_INTERNAL_ERROR;
  275.       goto exit;
  276.     }
  277.   [self writeText:in_strm];
  278.   NXSeek(in_strm, 0, NX_FROMSTART);
  279.   ret_val = 0;
  280.  start_searching:
  281.   if (NXUserAborted())
  282.     {
  283.       ret_val = SEARCH_ABORTED;
  284.       goto exit;
  285.     }
  286.   if (regexpr)
  287.     {
  288.       p = -1;
  289.       if (s1>=0)
  290.         p = re_search_pattern(&rpat, in_strm->buf_base, textLength, s1, e1, 0);
  291.       if (p==-1 && e2!=0)
  292.         {
  293.           s1 = s2;
  294.           e1 = e2;
  295.           s2 = e2 = 0;
  296.           goto start_searching;
  297.         }
  298.       if (p==-2)
  299.         {
  300.           ret_val = SEARCH_INTERNAL_ERROR;
  301.           goto exit;
  302.         }
  303.       if (p>-1)
  304.         {
  305.           *pos = p;
  306.           *size = re_match_pattern(&rpat, in_strm->buf_base, textLength, p, 0);
  307.           if (*size<0)
  308.             {
  309.               ret_val = SEARCH_INTERNAL_ERROR;
  310.               goto exit;
  311.             }
  312.           ret_val = 1;
  313.         }
  314.     }
  315.   else
  316.     {
  317.       p = 0;
  318.       if (s1>=0)
  319.         p = Misc_TBMKsearch_memory(lpat, in_strm->buf_base+s1, e1, 0, &position);
  320.       if (p<0)
  321.         {
  322.           ret_val = SEARCH_INTERNAL_ERROR;
  323.           goto exit;
  324.         }
  325.       if (p==0 && e2!=0)
  326.         {
  327.           s1 = s2;
  328.           e1 = e2;
  329.           s2 = e2 = 0;
  330.           goto start_searching;
  331.         }
  332.       if (p>0)
  333.         {
  334.           *pos = position+s1;
  335.           *size = plen;
  336.           ret_val = 1;
  337.         }
  338.    }
  339.  exit:
  340.   Misc_TBMKpattern_free(&lpat);
  341.   if (in_strm!=NULL)
  342.     NXCloseMemory(in_strm, NX_FREEBUFFER);
  343.   return ret_val;
  344. }
  345.  
  346. - (oneway void)selectTextFrom:(int)start to:(int)end
  347. {
  348.   if ([self isSelectable] && start<=end && 0<=start)
  349.     [self setSel:start :end];
  350. }
  351.  
  352. - (int)setRegExprSyntax:(int)syntax
  353. {
  354.   return re_set_syntax(syntax);
  355. }
  356.  
  357. - (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
  358. {
  359.   char text[spN.cp-sp0.cp+1];
  360.   [self getSubstring:text start:sp0.cp length:spN.cp-sp0.cp];
  361.   text[spN.cp-sp0.cp] = '\0';
  362.   if (*text!='\0')
  363.     {
  364.       [pboard declareTypes:&type num:1 owner:NULL];
  365.       [pboard writeType:type data:text length:spN.cp-sp0.cp];
  366.     }
  367. }
  368.  
  369. @end
  370.